package models

import (
	"context"
	"encoding/json"
	"github.com/beego/beego/v2/client/orm"
	"github.com/spf13/cast"
	"go-cmdb/pkg"
	"math/rand"
	"time"
)

type Form struct {
	BModel
	Name   string `orm:"column(name)" json:"name"`
	Status uint8  `orm:"column(status)" json:"status"` //0->删除1->正常
	Type   uint8  `orm:"column(type)" json:"type"`     //1-问卷|2->试卷
	Config string `orm:"column(config)" json:"config"` //配置
	UUID   string `orm:"column(uuid)" json:"uuid"`
}

func NewForm() *Form {
	return &Form{}
}

func (*Form) TableName() string {
	return FormTable
}

func (tb *Form) GetByPage(pageSize, pageIndex uint64) (lst []Form, count uint64, err error) {

	db := orm.NewOrm()
	offset := (pageIndex - 1) * pageSize
	q := db.QueryTable(NewForm()).Filter("status__gt", 0).OrderBy("-updated_at")
	var c int64
	c, err = q.Count()
	count = uint64(c)
	q.Offset(offset).Limit(pageSize).All(&lst)
	return

}

type AddFormReq struct {
	Name string `json:"name"`
	Type uint8  `json:"type"`
}

func (tb *Form) Add(uid uint64, req AddFormReq) (err error) {

	now := time.Now()
	db := orm.NewOrm()
	tb.Name = req.Name
	tb.Type = req.Type
	tb.CreatedAt = now
	tb.CreatedBy = uid
	tb.UpdatedAt = now
	tb.CreatedBy = uid
	tb.UUID = pkg.UUID()
	tb.Status = 1
	_, err = db.Insert(tb)

	return

}

type UpdateFormReq struct {
	Name string `json:"name"`
	ID   uint64 `json:"id"`
}

func (tb *Form) Find(id uint64) (m *Form, err error) {

	db := orm.NewOrm()
	m = NewForm()
	err = db.QueryTable(NewForm()).Filter("id", id).One(m)
	return

}

type RemoveFormReq struct {
	ID uint64 `json:"id"`
}
type EnableFormReq struct {
	ID     uint64 `json:"id"`
	Status uint8  `json:"status"`
}

func (tb *Form) CStatus(status uint8, id, uid uint64) (err error) {

	now := time.Now()
	tb.ID = id
	tb.UpdatedAt = now
	tb.UpdatedBy = uid
	tb.Status = status
	db := orm.NewOrm()
	_, err = db.Update(tb, "status", "updated_at", "updated_by")

	return
}
func (tb *Form) Update(uid uint64, req UpdateFormReq) (err error) {

	now := time.Now()
	db := orm.NewOrm()
	tb.Name = req.Name
	tb.ID = req.ID
	tb.UpdatedAt = now
	tb.UpdatedBy = uid

	_, err = db.Update(tb, "updated_at", "updated_by", "name")

	return

}

type SaveFormDesignReq struct {
	FormID uint64                `json:"form_id,omitempty"`
	Config string                `json:"config"`
	Topics []SaveFormDesignTopic `json:"topics"`
}

type SaveFormDesignTopic struct {
	ID     uint64 `json:"id"`
	Name   string `json:"name"`
	Type   string `json:"type"`
	UUID   string `json:"uuid"`
	Config string `json:"config"`
}

func (tb *Form) SaveDesign(uid uint64, req SaveFormDesignReq) error {

	db := orm.NewOrm()
	now := time.Now()
	return db.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		//更新配置
		f := NewForm()
		f.UpdatedBy = uid
		f.UpdatedAt = now
		f.Config = req.Config
		f.ID = req.FormID
		if _, err := txOrm.Update(f, "updated_by", "updated_at", "config"); err != nil {
			return err
		}

		o := NewFormOption()
		o.Status = 1

		//更新题目
		if _, err := txOrm.Raw("update "+FormTopicTable+" set updated_by=?,updated_at=?,status=0 where form_id=?", uid, now, req.FormID).Exec(); err != nil {
			return err
		}

		//具体题目具体分析
		for k, topic := range req.Topics {
			//
			curTopic := NewFormOption()
			curTopic.UpdatedAt = now
			curTopic.UpdatedBy = uid
			curTopic.FormID = req.FormID
			curTopic.Type = topic.Type
			curTopic.Name = topic.Name
			curTopic.Config = topic.Config
			curTopic.Status = 1
			curTopic.CreatedAt = now
			curTopic.CreatedBy = uid
			curTopic.ID = topic.ID
			curTopic.Sequence = uint64(k + 1)
			curTopic.UUID = topic.UUID

			if topic.ID > 0 {
				//更新
				if _, err := txOrm.Update(curTopic, "status", "updated_by", "updated_at", "config", "name", "sequence"); err != nil {
					return err
				}
				continue
			}

			//创建
			if _, err := txOrm.Insert(curTopic); err != nil {
				return err
			}

		}

		return nil

	})

}

type FormDesignDTO struct {
	FormID   uint64                   `json:"form_id,omitempty"`
	Config   map[string]interface{}   `json:"config"`
	Topics   []map[string]interface{} `json:"topics"`
	Type     uint8                    `json:"type"`
	FormName string                   `json:"form_name"`
}

func (tb *FormDesignDTO) ToJson() string {

	by, _ := json.Marshal(*tb)
	return string(by)
}

func (tb *Form) GetFormDesign(id uint64) (m FormDesignDTO, err error) {

	var (
		db      = orm.NewOrm()
		f       Form
		fTopics []FormTopic
	)

	err = db.QueryTable(NewForm()).Filter("id", id).One(&f)
	if err != nil {
		return
	}

	_, err = db.QueryTable(NewFormOption()).Filter("form_id", id).Filter("status__gt", 0).OrderBy("sequence").All(&fTopics)
	if err != nil {
		return
	}

	m.FormID = f.ID
	m.Type = f.Type
	m.Config = make(map[string]interface{})
	if len(f.Config) > 0 {
		json.Unmarshal([]byte(f.Config), &m.Config)
	}

	for _, t := range fTopics {
		tConfig := make(map[string]interface{}, 0)
		if len(f.Config) > 0 {
			json.Unmarshal([]byte(t.Config), &tConfig)
		}
		tConfig["id"] = t.ID
		tConfig["name"] = t.Name
		tConfig["type"] = t.Type
		m.Topics = append(m.Topics, tConfig)
	}
	if m.Topics == nil {
		m.Topics = make([]map[string]interface{}, 0)
	}
	return

}

func (tb *Form) GetFormDesignByUUID(uuid string) (m FormDesignDTO, err error) {

	db := orm.NewOrm()
	var (
		f         Form
		fTopics   []FormTopic
		topicNum  = 0
		topicPerm []int
	)
	err = db.QueryTable(NewForm()).Filter("uuid", uuid).One(&f)
	if err != nil {
		return
	}

	_, err = db.QueryTable(NewFormOption()).Filter("form_id", f.ID).Filter("status__gt", 0).OrderBy("sequence").All(&fTopics)
	if err != nil {
		return
	}
	topicNum = len(fTopics)
	topicPerm = rand.Perm(topicNum)
	m.FormID = f.ID
	m.Type = f.Type
	m.FormName = f.Name
	m.Config = make(map[string]interface{})
	if len(f.Config) > 0 {
		json.Unmarshal([]byte(f.Config), &m.Config)
	}

	for i := 0; i < topicNum; i++ {
		var (
			topic FormTopic
			index = i
		)
		if cast.ToInt(m.Config["rand"]) == 1 {
			index = topicPerm[i]
		}
		topic = fTopics[index]

		tConfig := make(map[string]interface{}, 0)
		if len(f.Config) > 0 {
			json.Unmarshal([]byte(topic.Config), &tConfig)
		}
		tConfig["id"] = topic.ID
		tConfig["name"] = topic.Name
		tConfig["type"] = topic.Type

		if topic.Type == "radio" || topic.Type == "checkbox" || topic.Type == "select" {

			if cast.ToInt(tConfig["rand"]) == 1 {
				var (
					//选项序列化
					opts       = tConfig["options"].([]interface{})
					optNum     = len(opts)
					optionPerm = rand.Perm(optNum)
					newOpts    = make([]interface{}, 0, optNum)
				)
				for j := 0; j < optNum; j++ {
					newOpts = append(newOpts, opts[optionPerm[j]])
				}
				opts = nil
				tConfig["options"] = newOpts
			}

		}

		m.Topics = append(m.Topics, tConfig)
	}
	if m.Topics == nil {
		m.Topics = make([]map[string]interface{}, 0)
	}
	return

}
